
class TemplateCompiler {

    constructor(el, vm) {
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        this.vm = vm;

        if (this.el) {
            let fragment = this.nodeToFragment(this.el);
            // console.log(fragment);
            this.compile(fragment);

            this.el.appendChild(fragment);
        }
    }

    /**工具方法 */
    isElementNode(node) {
        return node.nodeType === 1;
    }

    isTextElement(node) {
        return node.nodeType === 3;
    }

    toArray(likeArray){
        return [].slice.call(likeArray);
    }

    isDirective(str){
        return str.startsWith('v-');
    }

    /**核心方法 */
    nodeToFragment(node) {

        let fragement = document.createDocumentFragment();
        let child;
        // let a = node.firstChild;

        while (child = node.firstChild) {
            fragement.appendChild(child);
        }

        return fragement;
    }

    compile(parent) {
        let childNode = parent.childNodes;
        this.toArray(childNode).forEach((node) => {
            if(this.isElementNode(node)){
                this.compileElement(node);
            }else if(this.isTextElement(node)){
                //文本表达式验证
                let textReg = /\{\{(.+)\}\}/;
                let expr = node.textContent;
                
                if(textReg.test(expr)){
                    expr = RegExp.$1;
                    this.compileText(node, expr);
                }
            }

        });

    }

    compileElement(node){
        let arrs = node.attributes;
        this.toArray(arrs).forEach((attr) => {
            let attrName = attr.name;
            if(this.isDirective(attrName)){
                let type = attrName.substring(2);

                let expr = attr.value;

                CompilerUtils[type](node, this.vm, expr);
            }
        })
    }

    compileText(node, expr){
        CompilerUtils.text(node, this.vm, expr);
    }
}

let CompilerUtils = {
    text(node, vm, expr){
        let updaterFn = this.updater['textUpdater'];
        updaterFn && updaterFn(node, vm.$data[expr]);

        new Watcher(vm, expr, (newValue) => {
            //触发订阅时，更新数据到对应节点
            updaterFn && updaterFn(node, newValue);
        });
    },
    model(node , vm, expr){
        let updaterFn = this.updater['modelUpdater'];
        updaterFn && updaterFn(node, vm.$data[expr]);

        new Watcher(vm, expr, (newValue) => {
            //触发订阅时，更新数据到对应节点
            updaterFn && updaterFn(node, newValue);
        });

        // console.log(node)
        node.addEventListener('input', (e) => {
            vm.$data[expr] = e.target.value;
        },false);
    },

    //更新规则方法
    updater: {
        textUpdater(node, value){
            node.textContent = value;
        },
        modelUpdater(node, value){
            node.value = value;
        }
    }
}